#include "connections.h"

/* 
 * System wide module for keeping track of active connections.  
 * 
 * Some distinguished connections are:
 * 
 *    listener: the socket binded to the well-known port of
 *    the server.
 *
 *    clients: sockets connected to client applications. 
 *
 *    connected clients: clients' connections that are
 *    not logged into the chatroom.
 *
 *    logged clients: clients' connections that are
 *    logged into the chatroom. 
 *
 * It should be noticed that:
 *
 *    all = listener + clients
 *
 *    clients = connected + logged
 *
*/

/* 
 *    Private section of the module
*/

/* All sockets */
static fd_set _all;

/* Clients sockets */
static fd_set _clients;

/* Connected clients sockets */
static fd_set _connected;

/* Logged clients sockets */
static fd_set _logged;

/* Listener socket */
static int _listener;

/* Maximum socket file descriptor */
static int _fdmax;



/* 
 *    Public interface
*/

/* Module setup function */
void connections_setup() 
{
	FD_ZERO(&_all);
    FD_ZERO(&_clients);
	FD_ZERO(&_connected);
	FD_ZERO(&_logged);
	_listener = -1;
	_fdmax = -1;
}

/* Functions for registering and closing connections */

/* Resgisters the new connection refered to by conn_fd.
 * This connection is initially placed in "all" and "clients"
 * sets. 
*/
int connections_new(int conn_fd)
{
	FD_SET(conn_fd,&_all); 
	FD_SET(conn_fd,&_clients);     
	FD_SET(conn_fd,&_connected);
	_fdmax = MAX(_fdmax,conn_fd);
	
	return 0; 
}

/* Maximum fd */ 
int connections_max_fd()
{
	return _fdmax;
}

/* Removes the connection (file descriptor) from every set.
 * This funtions invokes close on the socket
*/
int connections_close(int conn_fd)
{   
	if (conn_fd == _listener)
		_listener = -1;
		
	FD_CLR(conn_fd,&_all);
    FD_CLR(conn_fd,&_clients);
	FD_CLR(conn_fd,&_connected);
	FD_CLR(conn_fd,&_logged);
	
	if (conn_fd >= _fdmax) {
		int i;
		for(i = conn_fd; !FD_ISSET(i,&_all); i--);
		_fdmax = i; 
	}
	
	return 0;	
}



/* Functions for the listener socket */
                                   
int connections_set_listener(int new_listener)
{
	_listener = new_listener;
	FD_SET(new_listener,&_all); 
	_fdmax = MAX(_fdmax,new_listener);
	
	return 0;	
}

int connections_get_listener()
{
	return _listener;
}                            



/* Functions for accesing different sets of connections */

connection_status_t connections_get_status(int conn_fd) 
{
	if (FD_ISSET(conn_fd,&_logged))
		return LOGGED;
	else if (FD_ISSET(conn_fd,&_connected))
		return CONNECTED;
	else
		return ERRONEOUS;
}  

/* Returns every connection (listener + clients) */
void connections_get_all(fd_set *fds)
{
	*fds = _all;
}

/* Returns all connections with clients */
void connections_get_clients(fd_set *fds) 
{
	*fds = _clients;
}

/* Returns clients' connections that are logged */
void connections_get_logged(fd_set *fds)
{
	*fds = _logged;
}
                                            
/* Returns clients' connections that are not logged */
void connections_get_connected(fd_set *fds)
{
	*fds = _connected;
}

/* Moves a connection form the connected list to 
 * the logged list. Returns 0 if success, 1 if the
 * was not in the connected list.
*/
int connections_log(int conn_fd)
{
	if (!FD_ISSET(conn_fd,&_connected))
		return 1;
		
	FD_CLR(conn_fd,&_connected);
	FD_SET(conn_fd,&_logged);
	
	return 0;
}

/* Moves a connection form the logged list to 
 * the connected list. Returns 0 if success, 1 if the
 * was not in the logged list.
*/
int connections_logout(int conn_fd)
{
	if (!FD_ISSET(conn_fd,&_logged))
		return 1;
		
	FD_CLR(conn_fd,&_logged);
	FD_SET(conn_fd,&_connected);
	
	return 0;	
}

/* Returns the fd_set resulting form subtracting fd 
 * to source. Simbolically: result = source - fd
*/
void connections_all_but(int fd, fd_set* source, fd_set *result)
{
	*result = *source;
	FD_CLR(fd, result);	
}